import os
import time
import base64
import tempfile
import json
import re
import subprocess
from typing import Optional, Tuple, List, Dict, Any
from mcp.server.fastmcp import FastMCP
from mcp.server.fastmcp import Image
from mcp.server.fastmcp.exceptions import FastMCPError
from ppadb.client import Client as AdbClient
from ppadb.device import Device


# Initialize FastMCP server
mcp = FastMCP("android_adb")


# Core helper functions
def get_adb_client() -> AdbClient:
    """Initialize and return ADB client"""
    subprocess.check_call(
        ["adb", "devices"],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        shell=True,
    )
    return AdbClient()


def get_device(device_id: Optional[str] = None) -> Device:
    """Get ADB device, return specified device if device_id provided, otherwise return first available device"""
    client = get_adb_client()
    devices = client.devices()

    if not devices:
        raise FastMCPError("No connected devices found")

    if device_id:
        for device in devices:
            if device.serial == device_id:
                return device
        raise FastMCPError(f"Device not found: {device_id}")

    return devices[0]


def safe_shell_command(device: Device, command: str) -> Optional[str]:
    """Execute shell command and return result, None if failed"""
    try:
        result = device.shell(command)
        return result.strip() if result else None
    except Exception:
        return None


def validate_package_name(package_name: str) -> None:
    """Validate package name format"""
    if not package_name or not re.match(r"^[a-zA-Z][\w.]+$", package_name):
        raise FastMCPError(f"Invalid package name: {package_name}")


def validate_timeout(timeout: int, min_val: int = 1, max_val: int = 300) -> None:
    """Validate timeout value"""
    if timeout < min_val or timeout > max_val:
        raise FastMCPError(
            f"Timeout must be between {min_val}-{max_val} seconds: {timeout}"
        )


def validate_coordinates(
    x: int,
    y: int,
    screen_width: int,
    screen_height: int,
    coord_name: str = "coordinates",
) -> None:
    """Validate if coordinates are within screen bounds"""
    if x < 0:
        raise FastMCPError(f"{coord_name} X coordinate cannot be negative: {x}")
    if y < 0:
        raise FastMCPError(f"{coord_name} Y coordinate cannot be negative: {y}")
    if x >= screen_width:
        raise FastMCPError(
            f"{coord_name} X coordinate {x} exceeds screen width range (0-{screen_width-1})"
        )
    if y >= screen_height:
        raise FastMCPError(
            f"{coord_name} Y coordinate {y} exceeds screen height range (0-{screen_height-1})"
        )


def get_screen_size(device: Device) -> Tuple[int, int]:
    """Get device screen dimensions"""
    wm_output = safe_shell_command(device, "wm size")
    if not wm_output:
        raise FastMCPError("Failed to get screen resolution")

    size_match = re.search(r"(\d+)x(\d+)", wm_output)
    if size_match:
        return int(size_match.group(1)), int(size_match.group(2))
    else:
        raise FastMCPError(f"Failed to parse screen resolution: {wm_output}")


def check_file_exists(device: Device, path: str) -> bool:
    """Check if file exists on device"""
    result = safe_shell_command(
        device, f"test -f {path} && echo 'exists' || echo 'not_exists'"
    )
    return result == "exists"


def check_directory_exists(device: Device, path: str) -> bool:
    """Check if directory exists on device"""
    result = safe_shell_command(
        device, f"test -d {path} && echo 'exists' || echo 'not_exists'"
    )
    return result == "exists"


def format_file_size(size_bytes: int) -> str:
    """Format file size in human readable format"""
    if size_bytes >= 1024 * 1024:
        return f"{size_bytes / (1024 * 1024):.2f} MB"
    elif size_bytes >= 1024:
        return f"{size_bytes / 1024:.2f} KB"
    else:
        return f"{size_bytes} bytes"


def cleanup_temp_file(file_path: Optional[str]) -> None:
    """Safely cleanup temporary file"""
    if file_path and os.path.exists(file_path):
        try:
            os.remove(file_path)
        except Exception:
            pass


def ensure_local_directory(file_path: str) -> None:
    """Ensure local directory exists for file path"""
    local_dir = os.path.dirname(file_path)
    if local_dir and not os.path.exists(local_dir):
        os.makedirs(local_dir, exist_ok=True)


def execute_with_error_handling(func, error_message: str):
    """Execute function with consistent error handling"""
    try:
        return func()
    except FastMCPError:
        raise
    except Exception as e:
        raise FastMCPError(f"{error_message}: {str(e)}")


def perform_swipe(
    device: Device,
    start_x: int,
    start_y: int,
    end_x: int,
    end_y: int,
    duration: int,
    direction: str,
) -> str:
    """Common swipe functionality"""
    screen_width, screen_height = get_screen_size(device)

    # Validate coordinates
    validate_coordinates(start_x, start_y, screen_width, screen_height, "start")
    validate_coordinates(end_x, end_y, screen_width, screen_height, "end")

    # Validate swipe direction
    if direction == "up" and end_y >= start_y:
        raise FastMCPError(
            f"For upward swipe, ending Y ({end_y}) should be less than starting Y ({start_y})"
        )
    elif direction == "down" and end_y <= start_y:
        raise FastMCPError(
            f"For downward swipe, ending Y ({end_y}) should be greater than starting Y ({start_y})"
        )
    elif direction == "left" and end_x >= start_x:
        raise FastMCPError(
            f"For left swipe, ending X ({end_x}) should be less than starting X ({start_x})"
        )
    elif direction == "right" and end_x <= start_x:
        raise FastMCPError(
            f"For right swipe, ending X ({end_x}) should be greater than starting X ({start_x})"
        )

    # Execute swipe
    device.shell(f"input swipe {start_x} {start_y} {end_x} {end_y} {duration}")
    return f"Successfully swiped {direction} from ({start_x}, {start_y}) to ({end_x}, {end_y}), screen resolution: {screen_width}x{screen_height}"


# Connection management tools
@mcp.tool()
def connect_remote_device(host: str, port: int = 5555) -> str:
    """Connect to remote Android device"""

    def connect():
        client = get_adb_client()
        result = client.remote_connect(host, port)
        if not result:
            raise FastMCPError(f"Failed to connect remote device {host}:{port}")
        return f"Successfully connected to remote device {host}:{port}"

    return execute_with_error_handling(connect, "Remote connection failed")


@mcp.tool()
def disconnect_remote_device(host: str = "172.31.46.1", port: int = 5555) -> str:
    """Disconnect from remote Android device"""

    def disconnect():
        client = get_adb_client()
        result = client.remote_disconnect(host, port)
        if not result:
            raise FastMCPError(f"Failed to disconnect remote device {host}:{port}")
        return f"Successfully disconnected from remote device {host}:{port}"

    return execute_with_error_handling(disconnect, "Remote disconnection failed")


@mcp.tool()
def list_devices() -> str:
    """List all connected Android devices"""

    def list_all():
        client = get_adb_client()
        devices = client.devices()

        if not devices:
            raise FastMCPError("No connected devices found")

        device_info = []
        for device in devices:
            try:
                model = (
                    safe_shell_command(device, "getprop ro.product.model") or "Unknown"
                )
                android_version = (
                    safe_shell_command(device, "getprop ro.build.version.release")
                    or "Unknown"
                )
                device_info.append(
                    f"Device ID: {device.serial}\nModel: {model}\nAndroid Version: {android_version}"
                )
            except Exception as e:
                device_info.append(
                    f"Device ID: {device.serial}\nError getting details: {str(e)}"
                )

        return "\n\n".join(device_info)

    return execute_with_error_handling(list_all, "Failed to list devices")


# Screen capture and recording tools
@mcp.tool()
def take_screenshot(device_id: Optional[str] = None) -> Image:
    """Take device screenshot"""
    temp_path = None
    try:
        device = get_device(device_id)

        # Create temporary file
        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
            temp_path = temp_file.name

        # Take screenshot on device
        screencap_result = safe_shell_command(
            device, "screencap -p /sdcard/screenshot.png"
        )
        if screencap_result and "error" in screencap_result.lower():
            raise FastMCPError(f"Device screenshot command failed: {screencap_result}")

        # Check if screenshot file exists
        if not check_file_exists(device, "/sdcard/screenshot.png"):
            raise FastMCPError("Screenshot file could not be created on device")

        # Pull screenshot file to local
        device.pull("/sdcard/screenshot.png", temp_path)

        # Verify local file
        if not os.path.exists(temp_path) or os.path.getsize(temp_path) == 0:
            raise FastMCPError("Screenshot file is empty or not created")

        # Read and validate image data
        with open(temp_path, "rb") as img_file:
            image_data = img_file.read()

        if not image_data or not image_data.startswith(b"\x89PNG\r\n\x1a\n"):
            raise FastMCPError("Invalid PNG format")

        # Clean up
        device.shell("rm /sdcard/screenshot.png")
        return Image(data=image_data, format="png")

    except FastMCPError:
        raise
    except Exception as e:
        raise FastMCPError(f"Screenshot failed: {str(e)}")
    finally:
        cleanup_temp_file(temp_path)


@mcp.tool()
def record_screen(path: str, duration: int = 5, device_id: Optional[str] = None) -> str:
    """Record device screen video"""

    def record():
        device = get_device(device_id)

        # Validate and prepare path
        full_path = (
            os.path.abspath(path)
            if os.path.isabs(path)
            else os.path.join(os.environ.get("ADB_RECORD_DIR", os.getcwd()), path)
        )
        if not full_path.lower().endswith(".mp4"):
            full_path += ".mp4"

        # Validate duration
        if duration <= 0 or duration > 180:
            raise FastMCPError(
                f"Recording duration must be between 1-180 seconds: {duration}"
            )

        # Ensure output directory exists and is writable
        output_dir = os.path.dirname(full_path)
        os.makedirs(output_dir, exist_ok=True)
        if not os.access(output_dir, os.W_OK):
            raise FastMCPError(f"Output directory is not writable: {output_dir}")

        # Record screen
        device.shell(f"screenrecord --time-limit {duration} /sdcard/screenrecord.mp4")

        # Wait for recording completion with progress
        for i in range(duration):
            time.sleep(1)
            print(f"Recording progress: {i+1}/{duration}s")
        time.sleep(2)  # Extra wait

        # Check and pull recorded file
        if not check_file_exists(device, "/sdcard/screenrecord.mp4"):
            raise FastMCPError("Recording failed - no file found on device")

        device.pull("/sdcard/screenrecord.mp4", full_path)

        # Verify local file
        if not os.path.exists(full_path) or os.path.getsize(full_path) == 0:
            raise FastMCPError("Local recorded file was not created or is empty")

        # Clean up and return result
        device.shell("rm /sdcard/screenrecord.mp4")
        file_size = os.path.getsize(full_path)

        return f"Screen recording successful!\nFile: {full_path}\nDuration: {duration}s\nSize: {format_file_size(file_size)}"

    return execute_with_error_handling(record, "Screen recording failed")


# Touch and interaction tools
@mcp.tool()
def tap_screen(x: int, y: int, device_id: Optional[str] = None) -> str:
    """Tap on the specified position on the screen"""

    def tap():
        device = get_device(device_id)
        screen_width, screen_height = get_screen_size(device)
        validate_coordinates(x, y, screen_width, screen_height)

        device.shell(f"input tap {x} {y}")
        return f"Successfully tapped at ({x}, {y}), screen resolution: {screen_width}x{screen_height}"

    return execute_with_error_handling(tap, "Tap screen failed")


@mcp.tool()
def multi_tap(taps: str, device_id: Optional[str] = None) -> str:
    """Multi-point tap on the screen"""

    def multi_tap_impl():
        device = get_device(device_id)
        screen_width, screen_height = get_screen_size(device)

        try:
            tap_points = json.loads(taps)
        except json.JSONDecodeError as e:
            raise FastMCPError(f"Invalid JSON format: {str(e)}")

        if not isinstance(tap_points, list):
            raise FastMCPError("Tap coordinates must be a list")

        results = []
        for i, point in enumerate(tap_points):
            if not isinstance(point, dict) or "x" not in point or "y" not in point:
                raise FastMCPError(
                    f"Invalid tap point {i+1}: must have x and y coordinates"
                )

            x, y = int(point["x"]), int(point["y"])
            validate_coordinates(x, y, screen_width, screen_height, f"Tap point {i+1}")

            device.shell(f"input tap {x} {y}")
            results.append(f"({x}, {y})")
            time.sleep(0.5)

        return f"Multi-tap successful: {', '.join(results)}, screen: {screen_width}x{screen_height}"

    return execute_with_error_handling(multi_tap_impl, "Multi-tap failed")


# Swipe operations - simplified using common function
@mcp.tool()
def swipe_up(
    start_x: int,
    start_y: int,
    end_y: int,
    duration: int = 300,
    device_id: Optional[str] = None,
) -> str:
    """Swipe up on the screen"""

    def swipe():
        device = get_device(device_id)
        return perform_swipe(device, start_x, start_y, start_x, end_y, duration, "up")

    return execute_with_error_handling(swipe, "Swipe up failed")


@mcp.tool()
def swipe_down(
    start_x: int,
    start_y: int,
    end_y: int,
    duration: int = 300,
    device_id: Optional[str] = None,
) -> str:
    """Swipe down on the screen"""

    def swipe():
        device = get_device(device_id)
        return perform_swipe(device, start_x, start_y, start_x, end_y, duration, "down")

    return execute_with_error_handling(swipe, "Swipe down failed")


@mcp.tool()
def swipe_left(
    start_x: int,
    start_y: int,
    end_x: int,
    duration: int = 300,
    device_id: Optional[str] = None,
) -> str:
    """Swipe left on the screen"""

    def swipe():
        device = get_device(device_id)
        return perform_swipe(device, start_x, start_y, end_x, start_y, duration, "left")

    return execute_with_error_handling(swipe, "Swipe left failed")


@mcp.tool()
def swipe_right(
    start_x: int,
    start_y: int,
    end_x: int,
    duration: int = 300,
    device_id: Optional[str] = None,
) -> str:
    """Swipe right on the screen"""

    def swipe():
        device = get_device(device_id)
        return perform_swipe(
            device, start_x, start_y, end_x, start_y, duration, "right"
        )

    return execute_with_error_handling(swipe, "Swipe right failed")


@mcp.tool()
def input_text(text: str, device_id: Optional[str] = None) -> str:
    """Input text on the device"""

    def input_impl():
        device = get_device(device_id)
        escaped_text = text.replace("'", "\\'").replace('"', '\\"').replace(" ", "%s")
        device.shell(f"input text '{escaped_text}'")
        return f"Successfully input text: {text}"

    return execute_with_error_handling(input_impl, "Text input failed")


# App management tools
@mcp.tool()
def start_app(
    package_name: str, device_id: Optional[str] = None, timeout: int = 10
) -> str:
    """Start an app and confirm launch"""

    def start():
        device = get_device(device_id)
        validate_package_name(package_name)
        validate_timeout(timeout, 1, 60)

        # Check if app is installed
        pkgs = safe_shell_command(device, "pm list packages")
        if not pkgs or f"package:{package_name}" not in pkgs:
            raise FastMCPError(f"App not installed: {package_name}")

        # Try launch methods
        launch_commands = [
            f"monkey -p {package_name} -c android.intent.category.LAUNCHER 1",
            f"am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER {package_name}",
        ]

        launched = False
        for cmd in launch_commands:
            out = safe_shell_command(device, cmd)
            if not out or not any(
                k in out.lower() for k in ["error", "fail", "exception"]
            ):
                launched = True
                break

        if not launched:
            raise FastMCPError(f"Failed to start app: {package_name}")

        # Wait for process and verify
        for i in range(timeout):
            time.sleep(1)
            ps = safe_shell_command(device, "ps")
            if ps and package_name in ps:
                return f"App launched: {package_name}\nTime: {i+1}s"

        raise FastMCPError(f"App launch timeout ({timeout}s): {package_name}")

    return execute_with_error_handling(start, "App launch failed")


@mcp.tool()
def kill_app(package_name: str, device_id: Optional[str] = None) -> str:
    """Force stop an app and verify if it was successfully stopped"""

    def kill():
        device = get_device(device_id)
        validate_package_name(package_name)

        # Check if app is running before stopping
        ps_before = safe_shell_command(device, "ps")
        if not ps_before:
            raise FastMCPError("Failed to get process list")

        is_running_before = package_name in ps_before

        # Execute force stop
        device.shell(f"am force-stop {package_name}")
        time.sleep(1)

        # Check if stopped
        ps_after = safe_shell_command(device, "ps")
        if not ps_after:
            raise FastMCPError("Failed to get process list")

        is_running_after = package_name in ps_after

        if not is_running_before:
            return f"App was not running: {package_name}"
        elif is_running_after:
            # Try alternative method
            device.shell(f"pkill -f {package_name}")
            time.sleep(1)
            ps_final = safe_shell_command(device, "ps")
            is_running_final = ps_final and package_name in ps_final

            if is_running_final:
                return f"App partially stopped: {package_name}"
            else:
                return f"App stopped using alternative method: {package_name}"
        else:
            return f"App successfully stopped: {package_name}"

    return execute_with_error_handling(kill, "Failed to stop app")


@mcp.tool()
def clear_app_data(package_name: str, device_id: Optional[str] = None) -> str:
    """Clear app data and verify operation success"""

    def clear():
        device = get_device(device_id)
        validate_package_name(package_name)

        # Check if app is installed
        packages_output = safe_shell_command(device, "pm list packages")
        if not packages_output or f"package:{package_name}" not in packages_output:
            raise FastMCPError(f"App not installed: {package_name}")

        # Execute clear command
        clear_output = safe_shell_command(device, f"pm clear {package_name}")

        # Check result
        if clear_output:
            clear_lower = clear_output.lower()
            if "success" in clear_lower:
                return f"Successfully cleared app data: {package_name}"
            elif any(
                error in clear_lower for error in ["error", "failed", "exception"]
            ):
                return f"Clear failed: {package_name}\nOutput: {clear_output}"

        return f"App data cleared: {package_name}"

    return execute_with_error_handling(clear, "Failed to clear app data")


# Device information tools
@mcp.tool()
def get_screen_resolution(device_id: Optional[str] = None) -> str:
    """Get screen resolution"""

    def get_resolution():
        device = get_device(device_id)
        output = safe_shell_command(device, "wm size")
        if not output:
            raise FastMCPError("Failed to get screen resolution")
        return f"Screen resolution: {output}"

    return execute_with_error_handling(
        get_resolution, "Failed to get screen resolution"
    )


@mcp.tool()
def get_device_info(device_id: Optional[str] = None) -> str:
    """Get detailed device information"""

    def get_info():
        device = get_device(device_id)

        # Device properties to collect
        props = {
            "Model": "ro.product.model",
            "Brand": "ro.product.brand",
            "Manufacturer": "ro.product.manufacturer",
            "Android Version": "ro.build.version.release",
            "API Level": "ro.build.version.sdk",
            "Device ID": "ro.serialno",
            "CPU Architecture": "ro.product.cpu.abi",
        }

        info = []
        for label, prop in props.items():
            value = safe_shell_command(device, f"getprop {prop}") or "Unknown"
            info.append(f"{label}: {value}")

        # Add screen resolution
        resolution = safe_shell_command(device, "wm size")
        info.append(f"Screen size: {resolution or 'Unknown'}")

        # Add battery info
        battery = safe_shell_command(device, "dumpsys battery | grep level")
        info.append(f"Battery: {battery or 'Unknown'}")

        return "\n".join(info)

    return execute_with_error_handling(get_info, "Failed to get device info")


@mcp.tool()
def list_installed_packages(device_id: Optional[str] = None) -> str:
    """List all installed application packages"""

    def list_packages():
        device = get_device(device_id)
        output = safe_shell_command(device, "pm list packages")
        if not output:
            raise FastMCPError("Failed to list packages")

        packages = [
            line.replace("package:", "").strip() for line in output.splitlines() if line
        ]
        return "\n".join(packages)

    return execute_with_error_handling(
        list_packages, "Failed to list installed packages"
    )


@mcp.tool()
def get_current_activity(device_id: Optional[str] = None) -> str:
    """Get the current foreground app and Activity"""

    def get_activity():
        device = get_device(device_id)

        # Try multiple sources for current activity
        sources = [
            "dumpsys activity activities",
            "dumpsys activity",
            "dumpsys window",
        ]

        component_pattern = re.compile(
            r"([a-zA-Z0-9_.$-]+(?:\.[a-zA-Z0-9_.$-]+)*)/([a-zA-Z0-9_.$-]+(?:\.[a-zA-Z0-9_.$-]+)*)"
        )

        for cmd in sources:
            output = safe_shell_command(device, cmd)
            if not output:
                continue

            # Look for relevant lines
            for line in output.splitlines():
                if any(
                    keyword in line
                    for keyword in [
                        "topResumedActivity",
                        "mResumedActivity",
                        "mFocusedApp",
                        "mCurrentFocus",
                    ]
                ):
                    match = component_pattern.search(line)
                    if match:
                        pkg, cls = match.groups()
                        if cls.startswith("."):
                            cls = pkg + cls
                        return (
                            f"Package: {pkg}\nActivity: {cls}\nComponent: {pkg}/{cls}"
                        )

        raise FastMCPError("Unable to determine current foreground activity")

    return execute_with_error_handling(get_activity, "Failed to get current activity")


# System management tools
@mcp.tool()
def reboot_device(device_id: Optional[str] = None, timeout: int = 120) -> str:
    """Reboot device and wait for completion"""

    def reboot():
        device = get_device(device_id)
        device_serial = device.serial
        validate_timeout(timeout, 1, 300)

        # Get boot info before reboot
        boot_id_before = (
            safe_shell_command(
                device, "cat /proc/sys/kernel/random/boot_id 2>/dev/null"
            )
            or "unknown"
        )

        print(f"Initiating device reboot: {device_serial}")

        # Execute reboot
        try:
            device.shell("reboot")
        except:
            pass  # Expected as device disconnects

        # Wait for reboot completion
        start_time = time.time()

        # Wait for device to go offline
        for i in range(30):
            time.sleep(1)
            try:
                client = get_adb_client()
                devices = client.devices()
                if not any(d.serial == device_serial for d in devices):
                    print(f"Device went offline after {i+1} seconds")
                    break
            except:
                break

        # Wait for device to come back online
        while time.time() - start_time < timeout:
            time.sleep(3)
            try:
                client = get_adb_client()
                devices = client.devices()

                target_device = next(
                    (d for d in devices if d.serial == device_serial), None
                )
                if target_device:
                    boot_completed = (
                        safe_shell_command(target_device, "getprop sys.boot_completed")
                        or "0"
                    )
                    boot_id_after = (
                        safe_shell_command(
                            target_device,
                            "cat /proc/sys/kernel/random/boot_id 2>/dev/null",
                        )
                        or "unknown"
                    )

                    if boot_completed == "1" and boot_id_after != boot_id_before:
                        elapsed = time.time() - start_time
                        return f"Device reboot completed!\nDevice: {device_serial}\nTime: {elapsed:.1f}s"
            except:
                continue

        raise FastMCPError(f"Device reboot timeout after {timeout}s: {device_serial}")

    return execute_with_error_handling(reboot, "Device reboot failed")


@mcp.tool()
def install_apk(apk_path: str, device_id: Optional[str] = None) -> str:
    """Install APK file"""

    def install():
        device = get_device(device_id)
        if not os.path.exists(apk_path):
            raise FastMCPError(f"APK file not found: {apk_path}")

        result = device.install(apk_path)
        if result:
            return f"APK installed successfully: {apk_path}"
        else:
            raise FastMCPError(f"APK installation failed: {apk_path}")

    return execute_with_error_handling(install, "APK installation failed")


@mcp.tool()
def uninstall_app(package_name: str, device_id: Optional[str] = None) -> str:
    """Uninstall an application"""

    def uninstall():
        device = get_device(device_id)
        validate_package_name(package_name)

        result = device.uninstall(package_name)
        if result:
            return f"App uninstalled successfully: {package_name}"
        else:
            raise FastMCPError(f"App uninstall failed: {package_name}")

    return execute_with_error_handling(uninstall, "App uninstall failed")


# Logging and performance tools
@mcp.tool()
def collect_device_logs(duration: int = 10, device_id: Optional[str] = None) -> str:
    """Collect device logs"""

    def collect():
        device = get_device(device_id)

        # Clear old logs and collect new ones
        device.shell("logcat -c")
        print(f"Collecting device logs for {duration} seconds...")
        time.sleep(duration)

        logs = safe_shell_command(device, "logcat -d -v threadtime") or "No logs found"

        # Truncate if too long
        if len(logs) > 1000:
            logs = "...\n[Log truncated]\n" + logs[-1000:]

        return logs

    return execute_with_error_handling(collect, "Log collection failed")


@mcp.tool()
def analyze_performance(
    package_name: str, duration: int = 10, device_id: Optional[str] = None
) -> str:
    """Analyze application performance"""

    def analyze():
        device = get_device(device_id)
        validate_package_name(package_name)

        # Start app
        device.shell(f"monkey -p {package_name} -c android.intent.category.LAUNCHER 1")
        time.sleep(2)

        # Collect performance data
        performance_data = []
        start_time = time.time()

        while time.time() - start_time < duration:
            mem_info = safe_shell_command(device, f"dumpsys meminfo {package_name}")
            cpu_info = (
                safe_shell_command(device, f"top -n 1 | grep {package_name}")
                or "Unknown"
            )

            timestamp = time.time() - start_time
            performance_data.append(
                f"Time: {timestamp:.2f}s\n"
                f"CPU: {cpu_info.strip()}\n"
                f"Memory: {' '.join(mem_info.split()[:20]) if mem_info else 'Unknown'}"
            )
            time.sleep(1)

        return "Performance Analysis:\n\n" + "\n\n".join(performance_data)

    return execute_with_error_handling(analyze, "Performance analysis failed")


# File management tools
@mcp.tool()
def list_files(dir_path: str = "/sdcard", device_id: Optional[str] = None) -> str:
    """List files and subdirectories"""

    def list_dir():
        device = get_device(device_id)

        if not check_directory_exists(device, dir_path):
            return f"Directory does not exist: {dir_path}"

        output = safe_shell_command(device, f"ls -la {dir_path}")
        if not output or not output.strip():
            return f"Directory is empty: {dir_path}"

        return output.strip()

    return execute_with_error_handling(list_dir, "Failed to list directory")


@mcp.tool()
def push_file(
    local_path: str, device_path: str, device_id: Optional[str] = None
) -> str:
    """Push a file to the device"""

    def push():
        device = get_device(device_id)

        if not os.path.exists(local_path) or not os.path.isfile(local_path):
            raise FastMCPError(f"Local file not found: {local_path}")

        # Ensure device directory exists
        device_dir = os.path.dirname(device_path)
        if device_dir and device_dir != "/":
            device.shell(f"mkdir -p {device_dir}")

        # Push file
        device.push(local_path, device_path)

        # Verify
        if check_file_exists(device, device_path):
            return f"Push successful: {local_path} -> {device_path}"
        else:
            raise FastMCPError(f"Push verification failed: {device_path}")

    return execute_with_error_handling(push, "Push file error")


@mcp.tool()
def pull_file(
    device_path: str, local_path: str, device_id: Optional[str] = None
) -> str:
    """Pull a file from the device"""

    def pull():
        device = get_device(device_id)

        if not check_file_exists(device, device_path):
            return f"File does not exist on device: {device_path}"

        # Ensure local directory exists
        ensure_local_directory(local_path)

        # Pull file
        device.pull(device_path, local_path)

        # Verify
        if os.path.exists(local_path):
            return f"Pull successful: {device_path} -> {local_path}"
        else:
            raise FastMCPError(f"Pull verification failed: {local_path}")

    return execute_with_error_handling(pull, "Pull file error")


@mcp.tool()
def delete_file(device_path: str, device_id: Optional[str] = None) -> str:
    """Delete a file or directory on the device"""

    def delete():
        device = get_device(device_id)

        is_file = check_file_exists(device, device_path)
        is_dir = check_directory_exists(device, device_path)

        if not is_file and not is_dir:
            return f"Path does not exist: {device_path}"

        # Delete file or directory
        cmd = f"rm -rf {device_path}" if is_dir else f"rm {device_path}"
        device.shell(cmd)

        # Verify deletion
        result = safe_shell_command(
            device, f"test -e {device_path} && echo 'exists' || echo 'deleted'"
        )
        if result == "deleted":
            return f"Successfully deleted: {device_path}"
        else:
            raise FastMCPError(f"Delete verification failed: {device_path}")

    return execute_with_error_handling(delete, "Delete operation failed")


@mcp.tool()
def make_directory(device_path: str, device_id: Optional[str] = None) -> str:
    """Create a directory on the device"""

    def mkdir():
        device = get_device(device_id)

        if check_directory_exists(device, device_path):
            return f"Directory already exists: {device_path}"

        device.shell(f"mkdir -p {device_path}")

        if check_directory_exists(device, device_path):
            return f"Directory created successfully: {device_path}"
        else:
            raise FastMCPError(f"Directory creation failed: {device_path}")

    return execute_with_error_handling(mkdir, "Create directory error")


@mcp.tool()
def run_adb_command(command: str, device_id: Optional[str] = None) -> str:
    """Run ADB command on the device"""

    def run_cmd():
        device = get_device(device_id)
        output = safe_shell_command(device, command)
        return output or "Command executed (no output)"

    return execute_with_error_handling(run_cmd, "ADB command execution failed")


# Run server
if __name__ == "__main__":
    mcp.run(transport="stdio")